package com.av.db.layer.impl;

import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.av.db.dataobjects.EntradaSalida;
import com.av.db.dataobjects.Tarjeta;
import com.av.db.layer.interfaces.EntradaSalidaLayer;
import com.av.exceptions.AvException;

/**
 * Clase que contiene la implementacion de funciones para la tabla de entradas y
 * salidas utilizando la tecnologia de Hibernate
 * 
 * @author Victor J Morales R
 * 
 */
public class EntradaSalidaLayerImpl extends HibernateDaoSupport implements
		EntradaSalidaLayer {

	private static Logger log = Logger.getLogger(EntradaSalidaLayerImpl.class);

	/**
	 * Actualiza una entrada o salida en la base de datos configurada
	 */
	@Override
	public void actualizar(EntradaSalida entradaSalida) throws AvException {
		log.info("Inicio - actualizar(EntradaSalida entradaSalida)");
		log.debug("Id : " + entradaSalida.getId());
		log.debug("Entrada/Salida : " + entradaSalida.toString());

		if (entradaSalida == null) {
			log.error("No se puede actualizar una entrada o salida nula");
			throw new AvException("Entrada o salida nula a ser actualizada");
		}

		if (entradaSalida.getId() < 0) {
			log.error("No se puede actualizar una entrada o salida con "
					+ "identificador invalido");
			throw new AvException("Entrada o salida con identificador invalido"
					+ " a ser actualizado");
		}

		getHibernateTemplate().update(entradaSalida);

		log.info("Fin - actualizar(EntradaSalida entradaSalida)");
	}// actualizar

	/**
	 * Agrega una entrada o salida en la base de datos configurada
	 */
	@Override
	public void agregar(EntradaSalida entradaSalida) throws AvException {
		log.info("Inicio - agregar(EntradaSalida entradaSalida)");
		log.debug("Entrada/Salida : " + entradaSalida.toString());

		if (entradaSalida == null) {
			log.error("No se puede agregar una entrada o salida nula");
			throw new AvException("Entrada o salida nula a ser agregada");
		}

		getHibernateTemplate().save(entradaSalida);

		log.info("Fin - agregar(EntradaSalida entradaSalida)");
	}// agregar

	/**
	 * Elimina una entrada o salida de la base de datos configurada
	 */
	@Override
	public void eliminar(EntradaSalida entradaSalida) throws AvException {
		log.info("Inicio - eliminar(EntradaSalida entradaSalida)");
		log.debug("Entrada/Salida : " + entradaSalida.toString());

		if (entradaSalida == null) {
			log.error("No se puede eliminar una entrada o salida nula");
			throw new AvException("Entrada o salida nula a ser eliminada");
		}

		if (entradaSalida.getId() < 0) {
			log.error("No se puede eliminar una entrada o salida con "
					+ "identificador negativo");
			throw new AvException("Identificador negativo");
		}

		getHibernateTemplate().delete(entradaSalida);

		log.info("Fin - eliminar(EntradaSalida entradaSalida)");
	}// eliminar

	/**
	 * Obtiene una entrada o salida de la base de datos configurada a partir de
	 * su identificador, nulo en caso de no entcontrarlo
	 */
	@Override
	public EntradaSalida obtener(int id) {
		log.info("Inicio - obtener(int id)");
		log.debug("Id : " + id);

		if (id < 0) {
			log.warn("No se puede obtener una entrada o salida con "
					+ "identificador negativo");
			return null;
		}

		EntradaSalida tmp = getHibernateTemplate().get(EntradaSalida.class, id);

		if (tmp != null) {
			log.debug("EntradaSalida : " + tmp.toString());
		} else {
			log.debug("Entrada/Salida no encontrada");
		}

		log.info("Fin - obtener(int id)");

		return tmp;
	}// obtener

	/**
	 * Funcion que obtiene el conjunto de todos las entradas y salidas
	 * registrados en la base de datos configurada
	 */
	@Override
	public EntradaSalida[] obtener() {
		log.info("Inicio - obtener()");

		List<?> l = getSession().createCriteria(EntradaSalida.class)
				.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).addOrder(
						Order.asc(EntradaSalida.ID)).list();

		EntradaSalida[] tmp = null;
		if (l != null && l.size() > 0) {
			log.debug("Cantidad obtenida : " + l.size());
			tmp = new EntradaSalida[l.size()];
			l.toArray(tmp);
		}

		log.info("Fin - obtener()");

		return tmp;
	}// obtener

	/**
	 * Funcion que obtiene la ultima entrada registrada para una tarjeta en
	 * particular
	 */
	@Override
	public EntradaSalida obtenerUltimaEntrada(Tarjeta t) {
		log.info("Inicio - obtenerUltimaEntrada(Tarjeta t)");
		log.debug("Tarjeta : " + t.toString());

		if (t == null) {
			log.warn("No se puede obtener una entrada o salida con de una "
					+ "tarjeta nula");
			return null;
		}

		List<?> l = getSession().createCriteria(EntradaSalida.class).add(
				Restrictions.eq(EntradaSalida.TARJETA, t))
				.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).addOrder(
						Order.desc(EntradaSalida.FECHA)).setMaxResults(1)
				.list();

		EntradaSalida tmp = null;
		if (l != null && l.size() > 0) {
			log.debug("Cantidad obtenida : " + l.size());
			tmp = (EntradaSalida) l.get(0);
			if (tmp != null) {
				log.debug("EntradaSalida : " + tmp.toString());
			} else {
				log.debug("Entrada/Salida no encontrada");
			}
		}

		log.info("Fin - obtenerUltimaEntrada(Tarjeta t)");

		return tmp;
	}
}// EntradaSalidaLayerImpl